<html><head><meta charset="utf-8"><title>实战：手写一个 markdown-loader-慕课专栏</title>
			<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
			<meta name="renderer" content="webkit">
			<meta property="qc:admins" content="77103107776157736375">
			<meta property="wb:webmaster" content="c4f857219bfae3cb">
			<meta http-equiv="Access-Control-Allow-Origin" content="*">
			<meta http-equiv="Cache-Control" content="no-transform ">
			<meta http-equiv="Cache-Control" content="no-siteapp">
			<link rel="apple-touch-icon" sizes="76x76" href="https://www.imooc.com/static/img/common/touch-icon-ipad.png">
			<link rel="apple-touch-icon" sizes="120x120" href="https://www.imooc.com/static/img/common/touch-icon-iphone-retina.png">
			<link rel="apple-touch-icon" sizes="152x152" href="https://www.imooc.com/static/img/common/touch-icon-ipad-retina.png">
			<link href="https://moco.imooc.com/captcha/style/captcha.min.css" rel="stylesheet">
			<link rel="stylesheet" href="https://www.imooc.com/static/moco/v1.0/dist/css/moco.min.css?t=201907021539" type="text/css">
			<link rel="stylesheet" href="https://www.imooc.com/static/lib/swiper/swiper-3.4.2.min.css?t=201907021539">
			<link rel="stylesheet" href="https://static.mukewang.com/static/css/??base.css,common/common-less.css?t=2.5,column/zhuanlanChapter-less.css?t=2.5,course/inc/course_tipoff-less.css?t=2.5?v=201907051055" type="text/css">
			<link charset="utf-8" rel="stylesheet" href="https://www.imooc.com/static/lib/ueditor/themes/imooc/css/ueditor.css?v=201907021539"><link rel="stylesheet" href="https://www.imooc.com/static/lib/baiduShare/api/css/share_style0_16.css?v=6aba13f0.css"></head>
			<body><div id="main">

<div class="container clearfix" id="top" style="display: block; width: 1134px;">
    
    <div class="center_con js-center_con l" style="width: 1134px;">
        <div class="article-con">
                            <!-- 买过的阅读 -->
                <div class="map">
                    <a href="/read" target="_blank"><i class="imv2-feather-o"></i></a>
                    <a href="/read/29" target="_blank">Webpack 从零入门到工程化实战</a>
                    <a href="" target="_blank">
                        <span>
                            / 5-2 实战：手写一个 markdown-loader
                        </span>
                    </a>
                </div>

            


            <div class="art-title" style="margin-top: 0px;">
                实战：手写一个 markdown-loader
            </div>
            <div class="art-info">
                
                <span>
                    更新时间：2019-07-08 14:12:13
                </span>
            </div>
            <div class="art-top">
                                <img src="https://img3.mukewang.com/5cd964af0001fdb906400360.jpg" alt="">
                                                <div class="famous-word-box">
                    <img src="https://www.imooc.com/static/img/column/bg-l.png" alt="" class="bg1 bg">
                    <img src="https://www.imooc.com/static/img/column/bg-r.png" alt="" class="bg2 bg">
                    <div class="famous-word">古之立大事者，不唯有超世之才，亦必有坚韧不拔之志。<p class="author">——苏轼</p></div>
                </div>
                            </div>
            <div class="art-content js-lookimg">
                <div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">根据上面的工作流程描述，我们知道在 Webpack 中，真正起编译作用的便是我们的 loader，<code>loader</code>实际就是处理单个模块的解析器（加载器不如解析器更好理解），平时我们进行 babel 的 ES6 编译，SCSS、LESS 等编译都是在 loader 里面完成的。loader 可以是异步的，也可以是同步的，同步的则直接返回处理后的模块内容，异步则调用异步回调函数输出处理后的模块内容。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">实际上，loader 只是一个普通的 <code>funciton</code>，它会传入匹配到的文件内容（String 类型的 source），<code>loader</code>做的是只需要对这些字符串做些处理就好了。本小节将从写一个 <code>markdown-loader</code>入手，介绍<code>loader</code>编写中常见的问题和工具。</p>
</div><div class="cl-preview-section"><h2 id="loader-的用法" style="font-size: 30px;">loader 的用法</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">首先回顾下 Webpack 中的 loader 有两种使用方式，然后是 loader 的链式调用和执行顺序。</p>
</div><div class="cl-preview-section"><h3 id="webpack-config-配置形式">webpack config 配置形式</h3>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token comment">// webpack.config.js</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
    entry<span class="token punctuation">:</span> <span class="token string">'./babel.js'</span><span class="token punctuation">,</span>
    mode<span class="token punctuation">:</span> <span class="token string">'development'</span><span class="token punctuation">,</span>
    devtool<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
    module<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        rules<span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token punctuation">{</span>
                test<span class="token punctuation">:</span> <span class="token regex">/\.js$/</span><span class="token punctuation">,</span>
                use<span class="token punctuation">:</span> <span class="token punctuation">[</span>
                    <span class="token punctuation">{</span>
                        loader<span class="token punctuation">:</span> <span class="token string">'babel-loader'</span><span class="token punctuation">,</span>
                        options<span class="token punctuation">:</span> <span class="token punctuation">{</span>
                            presets<span class="token punctuation">:</span> <span class="token punctuation">[</span>
                                <span class="token punctuation">[</span>
                                    <span class="token string">'@babel/preset-env'</span><span class="token punctuation">,</span>
                                    <span class="token punctuation">{</span>
                                        useBuiltIns<span class="token punctuation">:</span> <span class="token string">'usage'</span>
                                    <span class="token punctuation">}</span>
                                <span class="token punctuation">]</span>
                            <span class="token punctuation">]</span>
                        <span class="token punctuation">}</span>
                    <span class="token punctuation">}</span>
                <span class="token punctuation">]</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="内联-inline-写法">内联 inline 写法</h3>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> utils <span class="token keyword">from</span> <span class="token string">'raw-loader!../../utils.js'</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="loader-的链式调用">loader 的链式调用</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">loader 是支持链式调用的，例如下面两种形式的链式调用方法：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token comment">// webpack.config.js</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token comment">//   ...</span>
    module<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        rules<span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token punctuation">{</span>
                test<span class="token punctuation">:</span> <span class="token regex">/\.less$/</span><span class="token punctuation">,</span>
                use<span class="token punctuation">:</span> <span class="token punctuation">[</span>
                    <span class="token string">'style-loader'</span><span class="token punctuation">,</span>
                    <span class="token punctuation">{</span>
                        loader<span class="token punctuation">:</span> <span class="token string">'css-loader'</span><span class="token punctuation">,</span>
                        options<span class="token punctuation">:</span> <span class="token punctuation">{</span>
                            modules<span class="token punctuation">:</span> <span class="token boolean">true</span>
                        <span class="token punctuation">}</span>
                    <span class="token punctuation">}</span><span class="token punctuation">,</span>
                    <span class="token string">'less-loader'</span> <span class="token comment">// 将 Less 编译为 CSS</span>
                <span class="token punctuation">]</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">内联的链式调用写法：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> <span class="token string">'style-loader!css-loader!less-loader?a=b!../../common.less'</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="loader-的执行顺序">loader 的执行顺序</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在之前的章节介绍过 loader 的执行顺序是<strong>从后往前</strong>或者<strong>从右到左</strong>，即从最后的 loader 开始往反方向执行，例如内联的链式写法：<code>import 'style-loader!css-loader!less-loader?a=b!../../common.less';</code> 实际解析顺序是 Webpack 会首先读取<code>common.less</code>的内容（source），然后交给<code>less-loader</code>编译处理，<code>less-loader</code>接收到的是<code>common.less</code>的 source（内容），同时还能够接收到<code>a=b</code>的 options（选项参数），处理结束后将内容返回或者扔个<code>callback</code>即到达第二个<code>css-loader</code>处理，<code>css-loader</code>处理结束后会扔个<code>style-loader</code>，以此类推。</p>
</div><div class="cl-preview-section"><h2 id="loader-开发基础知识" style="font-size: 30px;">loader 开发基础知识</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在编写 loader 之前，我们需要先了解一些 loader 知识。</p>
</div><div class="cl-preview-section"><h3 id="loader-本质是一个函数">loader 本质是一个函数</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">loader 是本质上是一个函数，通过接受处理的内容，然后处理后返回结果。loader 的函数一般写法是：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">,</span> sourcemap<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 处理 content 操作...</span>
    <span class="token keyword">return</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在上面的示例中，我们看到 loader 实际是一个<code>funtion</code>，所以我们使用了 <code>return</code> 的方式返回 loader 处理后的数据。但其实这并<strong>不是我们最推荐的写法</strong>，在大多数情况下，我们还是更希望使用 <code>this.callback</code> 方法去返回数据。如果改成这种写法，示例代码可以改写为：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 处理 content 操作...</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>this.callback</code> 可以传入四个参数（其中后两个参数可以省略），分别是：</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><code>error</code>：当 loader 出错时向外抛出一个 <code>Error</code>对象，成功则传入<code>null</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>content</code>：经过 loader 编译后需要导出的内容，类型可以是为<code>String</code>或者<code>Buffer</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>sourceMap</code>：为方便调试生成的编译后内容的 source map；</li>
<li style="font-size: 20px; line-height: 38px;"><code>ast</code>: 本次编译生成的 AST 静态语法树，之后执行的 loader 可以直接使用这个 AST，可以省去重复生成 AST 的过程。</li>
</ul>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：一定要注意，编写 loader 的时候，如果要使用<code>this.callback</code>或者后面提到的<code>loader-utils</code>的<code>getOptions</code>等方法，<code>this</code>是 webpack 调用 loader 时候传入的自定义的特殊上下文，所以这时候<strong>不应该使用箭头函数</strong>！</p>
</blockquote>
</div><div class="cl-preview-section"><h3 id="loader-异步处理数据">loader 异步处理数据</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在上面的示例中，不论是使用 <code>return</code> 还是 <code>this.callback</code>，本质上都是同步的执行过程，假如我们的 loader 里存在异步操作，比如拉取异步的请求等又该怎么办呢？在 loader 中提供了两种异步写法。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">第一种是使用<code>async/await</code>异步函数写法：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">function</span> <span class="token function">timeout</span><span class="token punctuation">(</span>delay<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
            <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                <span class="token comment">// 模拟一些异步操作处理 content</span>
                <span class="token function">resolve</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span> delay<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">timeout</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> data<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">还有一种方式是使用<code>this.async</code>方法获取一个异步的<code>callback</code>，然后返回它，上面的示例代码使用<code>this.async</code>修改如下：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">function</span> <span class="token function">timeout</span><span class="token punctuation">(</span>delay<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> reject<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
            <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                <span class="token comment">// 模拟一些异步操作处理 content</span>
                <span class="token function">resolve</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span> delay<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">const</span> callback <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token keyword">async</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">timeout</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>data <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> data<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：<code>this.async</code>获取的 callback，参数也是跟<code>this.callback</code>的参数一致，即 error，content，sourcemap 和 ast。</p>
</blockquote>
</div><div class="cl-preview-section"><h3 id="处理二进制数据">处理二进制数据</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">像 <code>file-loader</code> 这样的 Loader，实际处理的内容是二进制数据，那么就需要通过设置<code>moudle.exports.raw = true;</code>来告诉 Webpack 给 loader 传入二进制格式的数据，代码可以如下：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>source <span class="token keyword">instanceof</span> <span class="token class-name">Buffer</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 一系列操作</span>
        <span class="token keyword">return</span> source<span class="token punctuation">;</span> <span class="token comment">// 本身也可以返回二进制数据提供给下一个loader</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
moudle<span class="token punctuation">.</span>exports<span class="token punctuation">.</span>raw <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token comment">// 不设置，就会拿到字符串</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="loader-的-pitch">loader 的 pitch</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">前面一直在提醒大家 loader 的执行顺序是<strong>从右到左</strong>的链式调用。这种说法实际说的是 loader 中 <code>module.exports</code> 出来的执行方法顺序。在一些场景下，loader 并不依赖上一个 loader 的结果，而只关心原输入内容。这时候，要拿到一开始的文件原内容，就需要使用 <code>module.exports.pitch = function();</code>， <code>pitch</code> 方法在 loader 中便是从左到右执行的，并且可以通过 data 这个变量来进行 <code>pitch</code> 和 <code>normal</code> 之间的传递。例如下面的代码：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span><span class="token function-variable function">pitch</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>remaining<span class="token punctuation">,</span> preceding<span class="token punctuation">,</span> data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">somothingFlag</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token string">'module.exports = require('</span> <span class="token operator">+</span> JSON<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token string">'-!'</span> <span class="token operator">+</span> remaining<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">');'</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    data<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>pitch</code>它可以接受三个参数，最重要的就是第三个参数 <code>data</code>，你可以为其挂在一些所需的值，一个 rule 里的所有的 loader 在执行时都能拿到这个值：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// this.data test</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'this data'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>data<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span>exports<span class="token punctuation">.</span><span class="token function-variable function">pitch</span> <span class="token operator">=</span> <span class="token punctuation">(</span>remaining<span class="token punctuation">,</span> preceding<span class="token punctuation">,</span> data<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    data<span class="token punctuation">.</span>value <span class="token operator">=</span> <span class="token string">'test'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="loader-的结果缓存">loader 的结果缓存</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">Webpack 增量编译机制会观察每次编译时的变更文件，在默认情况下，Webpack 会对 loader 的执行结果进行缓存，这样能够大幅度提升构建速度，不过我们也可以手动关闭它，示例代码如下：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 关闭loader缓存</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">cacheable</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> content<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="使用-webpack-的-loader-工具库">使用 Webpack 的 loader 工具库</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们在 <code>webpack.config.js</code> 书写 loader 配置时，经常会见到 <code>options</code> 这样一个配置项，或者在写内联调用 loader 的时候会通过<code>querystring</code>的形式传入 options，这就是 Webpack 为 loader 用户提供的自定义配置。在我们的 loader 里，可以拿到这些自定义配置。为了方便编写 loader，Webpack 官方将编写 loader 中常用的工具函数打包成了<code>loader-utils</code>和<code>schema-utils</code>模块，这里面包括了常用的获取 loader 选项（options）和参数验证等方法。</p>
</div><div class="cl-preview-section"><h4 id="loader-utils工具库" style="font-size: 26px;"><code>loader-utils</code>工具库</h4>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>loader-utils</code>提供了各种跟 loader 选项（options）相关的工具函数，例如下面的代码：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span> getOptions，stringifyRequest<span class="token punctuation">,</span> parseQuery  <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'loader-utils'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// getOptions 用于在loader里获取传入的options，返回的是对象值。</span>
    <span class="token keyword">const</span> options <span class="token operator">=</span> <span class="token function">getOptions</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// stringifyRequest转换路径，避免require()或impot时使用的绝对路径</span>
    <span class="token function">stringifyRequest</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token string">'./test.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Result =&gt;  "\"./test.js\""</span>

    <span class="token comment">// parseQuery获取query参数的，这个很简单就不说啦</span>
    <span class="token function">parseQuery</span><span class="token punctuation">(</span><span class="token string">'?name=kev&amp;age=14'</span><span class="token punctuation">)</span> <span class="token comment">// Result =&gt; {name: 'kev', age: '14'}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h4 id="schema-utils工具库" style="font-size: 26px;"><code>schema-utils</code>工具库</h4>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>schema-utils</code>是 loader 和 plugin 的参数认证器，检测传入的参数是否符合预期，基本用法是：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> validateOptions <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'schema-utils'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 下面是一个schema描述</span>
<span class="token keyword">const</span> schema <span class="token operator">=</span> <span class="token punctuation">{</span>
    type<span class="token punctuation">:</span> <span class="token string">'object'</span><span class="token punctuation">,</span>
    properties<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        name<span class="token punctuation">:</span> <span class="token punctuation">{</span>
            type<span class="token punctuation">:</span> <span class="token string">'string'</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        test<span class="token punctuation">:</span> <span class="token punctuation">{</span>
            anyOf<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span>type<span class="token punctuation">:</span> <span class="token string">'array'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>type<span class="token punctuation">:</span> <span class="token string">'string'</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token keyword">instanceof</span><span class="token punctuation">:</span> <span class="token string">'RegExp'</span><span class="token punctuation">}</span><span class="token punctuation">]</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        transform<span class="token punctuation">:</span> <span class="token punctuation">{</span>
            <span class="token keyword">instanceof</span><span class="token punctuation">:</span> <span class="token string">'Function'</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        sourceMap<span class="token punctuation">:</span> <span class="token punctuation">{</span>
            type<span class="token punctuation">:</span> <span class="token string">'boolean'</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    additionalProperties<span class="token punctuation">:</span> <span class="token boolean">false</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 验证参数的类型是否正确。</span>
    <span class="token function">validateOptions</span><span class="token punctuation">(</span>schema<span class="token punctuation">,</span> options<span class="token punctuation">,</span> <span class="token string">'loader name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="loader-中-this-相关的其它方法和属性">loader 中 this 相关的其它方法和属性</h3>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><code>this.context</code>: 当前处理转换的文件所在的目录；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.resource</code>: 当前处理转换的文件完整请求路径，包括 querystring；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.resourcePath</code>: 当前处理转换的文件的路径；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.resourceQuery</code>: 当前处理文件的 querystring；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.target</code>: Webpack 配置的 target；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.loadMoudle</code>: 处理文件时，需要依赖其它文件的处理结果时，可以使用 <code>this.loadMoudle(request: string, callback: function(err, source, sourceMap, module))</code>去获取到依赖文件的处理结果；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.resolve</code>: 获取指定文件的完整路径；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.addDependency</code>: 为当前处理文件添加依赖文件，以便依赖文件发生变化时重新调用 Loader 转换该文件，<code>this.addDependency(file: string)</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.addContextDependency</code>: 为当前处理文件添加依赖文件目录，以便依赖文件目录里文件发生变化时重新调用 Loader 转换该文件，<code>this.addContextDependency(dir: string)</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.clearDependencies</code>: 清除当前正在处理文件的所有依赖；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.emitFile</code>: 输出一个文件，使用的方法为 <code>this.emitFile(name: string, content: Buffer | string, sourceMap: {...})</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>this.emitError</code>：发送一个错误信息。</li>
</ul>
</div><div class="cl-preview-section"><h2 id="编写个-markdown-loader" style="font-size: 30px;">编写个 markdown loader</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">现在我们来手动写个 markdown-loader。Markdown-loader 是将 <a href="https://zh.wikipedia.org/zh-hans/Markdown">markdown</a> 语法的文件转换成 HTML，这里使用的是<a href="https://github.com/showdownjs/showdown">showdown</a>来转换 markdown 到 HTML。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">首先创建一个<code>markdown-loader</code>文件夹，然后添加<code>package.json</code>：</p>
</div><div class="cl-preview-section"><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">mkdir</span> markdown-loader <span class="token operator">&amp;&amp;</span> <span class="token function">cd</span> <span class="token variable">$_</span>
<span class="token function">npm</span> init -y
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">安装依赖 showdown 和 loader-utils，这里需要添加到<code>dependencies</code>，所以用<code>--save</code>保存到<code>package.json</code>：</p>
</div><div class="cl-preview-section"><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">npm</span> <span class="token function">install</span> showdown loader-utils --save
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">下面就是 markdown-loader 的<code>index.js</code>内容，几行就可以搞定一个 loader！</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token comment">// index.js</span>
<span class="token comment">// 引入依赖</span>
<span class="token keyword">const</span> showdown <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'showdown'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> loaderUtils <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'loader-utils'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 获取 options</span>
    <span class="token keyword">const</span> options <span class="token operator">=</span> loaderUtils<span class="token punctuation">.</span><span class="token function">getOptions</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 设置 cache</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">cacheable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 初始化 showdown 转换器</span>
    <span class="token keyword">const</span> converter <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">showdown<span class="token punctuation">.</span>Converter</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 处理 content</span>
    content <span class="token operator">=</span> converter<span class="token punctuation">.</span><span class="token function">makeHtml</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 返回结果</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> content<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">loader 写完了测试需要使用<code>require.resolve()</code>方法来使用本地的地址，现在创建一个<code>test</code>文件夹，添加<code>webpack.config.js</code>内容，loader中添加了<code>options</code>用于测试我们的markdown-loader能不能接收 options 参数：</p>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
    entry<span class="token punctuation">:</span> <span class="token string">'./index.js'</span><span class="token punctuation">,</span>
    module<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        rules<span class="token punctuation">:</span> <span class="token punctuation">[</span>
            <span class="token punctuation">{</span>
                test<span class="token punctuation">:</span> <span class="token regex">/\.md$/</span><span class="token punctuation">,</span>
                use<span class="token punctuation">:</span> <span class="token punctuation">[</span>
                    <span class="token string">'html-loader'</span><span class="token punctuation">,</span>
                    <span class="token punctuation">{</span>
                        loader<span class="token punctuation">:</span> require<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">'../index'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                        options<span class="token punctuation">:</span> <span class="token punctuation">{</span>
                            simplifiedAutoLink<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
                            tables<span class="token punctuation">:</span> <span class="token boolean">true</span>
                        <span class="token punctuation">}</span>
                    <span class="token punctuation">}</span>
                <span class="token punctuation">]</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">然后添加<code>markdown.md</code>和<code>index.js</code></p>
</div><div class="cl-preview-section"><pre class=" language-markdown"><code class="prism  language-markdown"><span class="token title important"><span class="token punctuation">#</span> 测试 markdown</span>

测试自动options 生效，自动检测网址添加 link：www.google.com

<span class="token title important"><span class="token punctuation">##</span> table</span>

| h1    |   h2    |      h3 |
| :---- | :-----: | ------: |
| 100   | <span class="token url">[a][<span class="token variable">1</span>]</span>  | <span class="token url">![b][<span class="token variable">2</span>]</span> |
| <span class="token italic"><span class="token punctuation">*</span>foo<span class="token punctuation">*</span></span> | <span class="token bold"><span class="token punctuation">**</span>bar<span class="token punctuation">**</span></span> | ~~baz~~ |
</code></pre>
</div><div class="cl-preview-section"><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> html <span class="token keyword">from</span> <span class="token string">'./markdown.md'</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>html<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">配置完成之后，安装<code>webpack</code>、<code>webpack-cli</code>和<code>html-loader</code>，然后执行<code>npx webpack</code>，编译后<code>markdown.md</code>的内容被转义成了HTML：</p>
</div><div class="cl-preview-section"><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>markdown<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>测试 markdown<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>测试自动options 生效，自动检测网址添加 link：<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.google.com<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>www.google.com<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>table<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>thead</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>left<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>h1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>h2<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>right<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>h3<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>thead</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tbody</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>left<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>100<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>[a][1]<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>right<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>![b][2]<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>left<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">&gt;</span></span>foo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>center<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>bar<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">text-align</span><span class="token punctuation">:</span>right<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>~~baz~~<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tbody</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>table</span><span class="token punctuation">&gt;</span></span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">到此为止，我们就完成了一个简单的 loader：<code>markdown-loader</code>了。</p>
</div><div class="cl-preview-section"><h2 id="总结" style="font-size: 30px;">总结</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">本小节主要让大家了解 Webapck 的 loader 编写知识，loader 本质上是一个函数，这个函数主要接收的参数是需要处理的文件内容，经过异步和同步处理内容之后通过<code>callback</code>的方式传递给后面的 loader 或者 Webpack 直接使用。loader 的链式调用顺序是从右到左的，但是也有<code>pitch</code>函数可以做到从左到右，这样可以在一些特殊场景中获取原来的文件内容，通过<code>data</code>进行传值。最后介绍了 loader-utils和 schema-utils两个在编写 loader 中常用的工具函数，介绍完这些loader 开发的基础知识之后，小节最后使用了<code>showdown</code>来编写了一个 markdown-loader，通过实际代码帮助大家理解 loader 知识。</p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">本小节 Webpack 相关面试题：</p>
<ol>
<li style="font-size: 20px; line-height: 38px;">编写过 Webpack loader吗？</li>
</ol>
</blockquote>
</div></div>
            </div>
                            <!-- 买过的阅读 -->
                <div class="art-next-prev clearfix">
                                                                        <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/29/article/287">
                                                    <div class="prev l clearfix">
                                <div class="icon l">
                                    <i class="imv2-arrow3_l"></i>
                                </div>
                                <p>
                                    实战：使用 PostCSS 打造移动适配方案
                                </p>
                            </div>
                        </a>
                                                                                            <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/29/article/289">
                                                    <div class="next r clearfix">
                                <p>
                                    实战：手写一个 prefetch-webpack-plugin 插件
                                </p>
                                <div class="icon r">
                                    <i class="imv2-arrow3_r"></i>
                                </div>

                            </div>
                        </a>
                                    </div>
                    </div>
        <div class="comments-con js-comments-con" id="coments_con">
        </div>



    </div>
    
    
    

</div>
 
<!-- 专栏介绍页专栏评价 -->

<!-- 专栏介绍页底部三条评价 -->

<!-- 专栏阅读页弹层目录和介绍页页面目录 -->

<!-- 专栏阅读页发布回复 -->

<!-- 专栏阅读页发布评论 -->

<!-- 专栏阅读页底部评论 -->

<!-- 专栏阅读 单个 评论 -->

<!-- 新增回复和展开三条以外回复 -->

<!-- 立即订阅的弹窗 -->












</div></body></html>